﻿#region Copyrights Notice
//©2010 Microsoft Corporation. All rights reserved.
#endregion

#region usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Configuration;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
using System.Xml.XPath;
using System.IO;
#endregion

namespace Microsoft.InformationSecurity.CISF.Common.Utilities
{
    public class SiteMapManager
    {
        public string docName = String.Empty;

        private const String SiteMapNodeName = "siteMapNode";

        #region Sitemap Management Functions

        public bool SetSiteMapNode(string value)
        {
            XmlDocument cfgDoc = new XmlDocument();
            loadConfigDoc(cfgDoc);

            //set the navigator
            XPathNavigator navigator = cfgDoc.CreateNavigator();

            XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
            manager.AddNamespace("ab", "http://schemas.microsoft.com/AspNet/SiteMap-File-1.0");

            // retrieve the selected node 
            XPathNavigator node = navigator.SelectSingleNode("//ab:siteMapNode[@url='~/Applications/Default.aspx']", manager);
            if (node == null)
                node = navigator.SelectSingleNode("//ab:siteMapNode[@url='/Applications/Default.aspx']", manager);
            try
            {
                //add sitemap entry by default 
                bool flag = true;

                //iterate the existing nodes and check if the sitemap entry already exists
                if (node.HasChildren)
                {
                    //populating current node iterator
                    XPathNodeIterator currinode = node.SelectChildren(node.NodeType);
                    while (currinode.MoveNext())
                    {
                        try
                        {
                            XmlNode currnode = ((System.Xml.IHasXmlNode)currinode.Current).GetNode();
                            string currvalue1 = currnode.Attributes["siteMapFile"].Value.ToString();

                            // Check if the value is not already set
                            if (currvalue1 == value)
                            {
                                flag = false;
                                break;
                            }
                        }
                        catch
                        {
                            //nothing... skip step
                        }
                    }
                }

                //check if the flag is turned ON?
                if (flag == true)
                {
                    // XPath select 
                    XmlNode onode = cfgDoc.SelectSingleNode("//ab:siteMapNode[@url='/Applications/Default.aspx']", manager);
                    if (onode == null)
                        onode = cfgDoc.SelectSingleNode("//ab:siteMapNode[@url='~/Applications/Default.aspx']", manager);

                    XmlElement childElement = onode.OwnerDocument.CreateElement("siteMapNode", manager.LookupNamespace("ab"));
                    if (childElement != null)
                    {
                        childElement.SetAttribute("siteMapFile", value);
                        onode.AppendChild(childElement);
                    }
                    //save the config file
                    saveConfigDoc(cfgDoc, docName);
                }
                //finally save the sitemap file
                return true;
            }
            catch
            {
                Console.WriteLine("Selected node is not found in the sitemap.");
                //throw new System.InvalidOperationException("Selected node is not found in the sitemap.");
                return false;
            }
        }

        public XmlElement GetSiteMapNode(string fullpath)
        {
            try
            {
                //set the document to load
                if (!string.IsNullOrEmpty(fullpath))
                    docName = fullpath + "//appweb.sitemap";

                XmlDocument cfgDoc = new XmlDocument();
                loadConfigDoc(cfgDoc);
                
                //get the first node from the xml document
                XmlElement smRootElement = (XmlElement)cfgDoc.GetElementsByTagName("siteMapNode")[0];

                // retrieve the selected element
                if (smRootElement == null)
                {
                    throw new System.InvalidOperationException("Selected node is not found in the sitemap.");
                }
                else
                {
                    return smRootElement;
                }
            }
            catch
            {
                throw;
                //throw new System.InvalidOperationException("Sitemap file is not found or cannot be loaded.");
            }
        }

        public bool RemoveSiteMapNode(string value)
        {
            XmlDocument cfgDoc = new XmlDocument();
            loadConfigDoc(cfgDoc);

            //set the navigator
            XPathNavigator navigator = cfgDoc.CreateNavigator();

            XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
            manager.AddNamespace("ab", "http://schemas.microsoft.com/AspNet/SiteMap-File-1.0");

            // retrieve the selected node 
            XPathNavigator node = navigator.SelectSingleNode("//ab:siteMapNode[@url='~/Applications/Default.aspx']", manager);
            if (node == null)
                node = navigator.SelectSingleNode("//ab:siteMapNode[@url='/Applications/Default.aspx']", manager);
            if (node == null)
            {
                throw new System.InvalidOperationException("selected node not found");
            }
            //else continue
            try
            {
                // XPath select 
                XmlNode onode = cfgDoc.SelectSingleNode("//ab:siteMapNode[@url='~/Applications/Default.aspx']", manager);
                if (onode == null)
                    onode = cfgDoc.SelectSingleNode("//ab:siteMapNode[@url='/Applications/Default.aspx']", manager);

                XmlNode onode1 = cfgDoc.SelectSingleNode("//ab:siteMapNode[@siteMapFile='" + value + "']", manager);

                onode.RemoveChild(onode1);

                //save it
                saveConfigDoc(cfgDoc, docName);
                return true;
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region Deployer_Sitemap_Functions

        public bool Deployer_UpdateApplicationSitemapLinks(string strAppName, string strAppFolderName)
        {
            try
            {
                if (string.IsNullOrEmpty(strAppName))
                    throw new ArgumentNullException("AppName", "Deployer_UpdateApplicationSitemapLinks: strAppName is null or empty.");
                if (string.IsNullOrEmpty(strAppFolderName))
                    throw new ArgumentNullException("AppFolderName", "Deployer_UpdateApplicationSitemapLinks: strAppFolderName is null or empty.");

                //set the privatePath value
                string appPath = "/Applications/" + strAppFolderName + "/";
                XmlNode currnode = null;
                XPathNodeIterator currinode = null;

                XmlDocument cfgDoc = new XmlDocument();
                loadConfigDoc(cfgDoc);

                //set the navigator
                XPathNavigator navigator = cfgDoc.CreateNavigator();

                XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
                manager.AddNamespace("ab", "http://schemas.microsoft.com/AspNet/SiteMap-File-1.0");

                // retrieve the selected node 
                XPathNavigator node = navigator.SelectSingleNode("//ab:siteMapNode[@title='" + strAppName + "']", manager);
                if (node == null)
                {
                    throw new System.InvalidOperationException("AppWeb.Sitemap Error: <Application Name> not found in the sitemap");
                }
                else
                {
                    //populating current node iterator
                    currinode = node.Select(".");
                    currnode = ((System.Xml.IHasXmlNode)currinode.Current).GetNode();
                    string currvalue = currnode.Attributes["url"].Value.ToString();

                    // Check if the value is not already set
                    if (!currvalue.StartsWith(appPath))
                    {
                        if (currvalue.StartsWith("~/"))
                            currnode.Attributes["url"].Value = appPath + currvalue.Substring(2);
                        else if (currvalue.StartsWith("/"))
                            currnode.Attributes["url"].Value = appPath + currvalue.Substring(1);
                        else
                            currnode.Attributes["url"].Value = appPath + currvalue;
                    }
                }
                //Continue updating the child nodes
                if (node.HasChildren)
                {
                    //populating current node iterator
                    currinode = node.SelectChildren(node.NodeType);
                    while (currinode.MoveNext())
                    {
                        try
                        {
                            currnode = ((System.Xml.IHasXmlNode)currinode.Current).GetNode();

                            string currvalue1 = currnode.Attributes["url"].Value.ToString();

                            // Check if the value is not already set
                            if (!currvalue1.StartsWith(appPath))
                            {
                                if (currvalue1.StartsWith("~/"))
                                    currnode.Attributes["url"].Value = appPath + currvalue1.Substring(2);
                                else if (currvalue1.StartsWith("/"))
                                    currnode.Attributes["url"].Value = appPath + currvalue1.Substring(1);
                                else
                                    currnode.Attributes["url"].Value = appPath + currvalue1;
                            }
                        }
                        catch
                        {
                            return false;
                        }
                    }
                }
                //finally save the doc
                saveConfigDoc(cfgDoc, docName);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        public bool Deployer_UpdateApplicationSitemapNodes(String title, String url, String description, bool actionFlag)
        {
            try
            {
                //doc name already set... loading the document now
                XmlDocument cfgDoc = new XmlDocument();
                loadConfigDoc(cfgDoc);

                //set the navigator
                XPathNavigator navigator = cfgDoc.CreateNavigator();

                XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
                manager.AddNamespace("ab", "http://schemas.microsoft.com/AspNet/SiteMap-File-1.0");

                // retrieve the selected node 
                XPathNavigator node = navigator.SelectSingleNode("//ab:siteMapNode[@url='/Applications/Default.aspx']", manager);
                if (node == null)
                    node = navigator.SelectSingleNode("//ab:siteMapNode[@url='~/Applications/Default.aspx']", manager);

                if (node == null)
                {
                    throw new System.InvalidOperationException("AppWeb.Sitemap Error: <Application Name> not found in the sitemap");
                }
                //else continue

                //add sitemap entry by default 
                bool flag = true;

                //iterate the existing nodes and check if the sitemap entry already exists
                if (node.HasChildren)
                {
                    //populating current node iterator
                    XPathNodeIterator currinode = node.SelectChildren(node.NodeType);
                    while (currinode.MoveNext())
                    {
                        try
                        {
                            XmlNode currnode = ((System.Xml.IHasXmlNode)currinode.Current).GetNode();
                            string currvalue = currnode.Attributes["title"].Value.ToString();

                            // Check if the value is not already set
                            if (currvalue == title)
                            {
                                //if the action is false <REMOVE> the node... <NOT ADD> the node.
                                if(!actionFlag)
                                {
                                    currinode.Current.DeleteSelf();
                                    saveConfigDoc(cfgDoc, docName);
                                }
                                //flag to skip the add portion for the current node
                                flag = false;
                                break;
                            }
                        }
                        catch
                        {
                            //nothing...
                        }
                    }
                }

                //check if the flag is turned ON?
                if (flag == true)
                {
                    XmlNode onode = cfgDoc.SelectSingleNode("//ab:siteMapNode[@url='/Applications/Default.aspx']", manager);
                    if (onode == null)
                        onode = cfgDoc.SelectSingleNode("//ab:siteMapNode[@url='~/Applications/Default.aspx']", manager);

                    XmlElement childElement = onode.OwnerDocument.CreateElement("siteMapNode", manager.LookupNamespace("ab"));
                    if (childElement != null)
                    {
                        childElement.SetAttribute("title", title);
                        childElement.SetAttribute("url", url);
                        childElement.SetAttribute("description", description);
                        childElement.SetAttribute("roles", "*");
                        onode.AppendChild(childElement);
                    }
                    //save it
                    saveConfigDoc(cfgDoc, docName);
                }
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        public bool Deployer_UpdateAppSitemapNodesWithArchivedApps()
        {
            try
            {
                //doc name already set... loading the document now
                XmlDocument cfgDoc = new XmlDocument();
                loadConfigDoc(cfgDoc);

                //set the navigator
                XPathNavigator navigator = cfgDoc.CreateNavigator();

                XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
                manager.AddNamespace("ab", "http://schemas.microsoft.com/AspNet/SiteMap-File-1.0");

                // retrieve the selected node 
                XPathNavigator node = navigator.SelectSingleNode("//ab:siteMapNode[@url='/Applications/Default.aspx']", manager);
                if(node == null)
                    node = navigator.SelectSingleNode("//ab:siteMapNode[@url='~/Applications/Default.aspx']", manager);

                if (node == null)
                {
                    throw new System.InvalidOperationException("AppWeb.Sitemap Error: <Application Name> not found in the sitemap");
                }
                //else continue

                //first remove all existing child nodes
                XmlNodeList aElements = cfgDoc.SelectNodes("//ab:siteMapNode[@siteMapFile='AppWeb.Sitemap']", manager); ;
                foreach (XmlNode currnode in aElements)
                {
                    currnode.ParentNode.RemoveChild(currnode);
                }
                //save it
                saveConfigDoc(cfgDoc, docName);

                //Start adding the new entries
                string[] strTitles = ConfigurationManager.AppSettings["ArchivedAppTitles"].ToString().Split(',');
                string[] strLinks = ConfigurationManager.AppSettings["ArchivedAppLinks"].ToString().Split(',');
                Deployer_UpdateApplicationSitemapNodes(strTitles[0], strLinks[0], strTitles[0], true);
                Deployer_UpdateApplicationSitemapNodes(strTitles[1], strLinks[1], strTitles[1], true);
                Deployer_UpdateApplicationSitemapNodes(strTitles[2], strLinks[2], strTitles[2], true);

                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        #endregion

        #region Helper functions
        private void saveConfigDoc(XmlDocument cfgDoc, string cfgDocPath)
        {
            try
            {
                XmlTextWriter writer = new XmlTextWriter(cfgDocPath, null);
                writer.Formatting = Formatting.Indented;
                cfgDoc.WriteTo(writer);
                writer.Flush();
                writer.Close();
                return;
            }
            catch
            {
                throw;
            }
        }
        private XmlDocument loadConfigDoc(XmlDocument cfgDoc)
        {
            // load the sitemap file if docName is NULL
            if (string.IsNullOrEmpty(docName))
            {
                docName = System.Web.HttpContext.Current.Server.MapPath("~//web.sitemap");
            }
            cfgDoc.Load(docName);
            return cfgDoc;
        }
        #endregion

    }
}
